#include <cstdio>                                                                                                      
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <queue>
#include "wn.h"
#include <map>
#define NOUN 1

using namespace std;
int directMultiCount;

static string do_is_defined(char *searchword)
{
    int i, found = 0;
    unsigned int search;
    char *morphword;

    /* Print all valid searches for word in all parts of speech */

    strtolower(strsubst(searchword, ' ', '_'));
	    
    i = NOUN; //noun
    if ((search = is_defined(searchword, i)) != 0) 
    {
        return searchword;
    } 
    return "";
}
typedef struct{
    long hereiam;
    string defn;
    vector<string> words;
} ANCESTOR;
typedef struct{
    long hereiam;		/* current file position */
    string defn;			/* synset gloss (definition) */
    vector<string> words;
    vector<ANCESTOR> ancestors;
} NODE;
typedef struct{
    string def;
    string example;
    string words;
} DOC;

void getAncestors(SynsetPtr synsetPtr, vector<ANCESTOR> &ancestors)
{
    for (int i = 0; i < synsetPtr->ptrcount; ++i)
    {
        if (synsetPtr->ptrtyp[i] == HYPERPTR)
        {
            ANCESTOR ancestor;
            int pos = synsetPtr->ppos[i]; 
            int offset = synsetPtr->ptroff[i];
            SynsetPtr freePtr = read_synset(pos, offset, "");
            synsetPtr = freePtr;
            ancestor.hereiam = synsetPtr->hereiam;
            ancestor.defn = string(synsetPtr->defn);
            for (int j = 0; j < synsetPtr->wcount; ++j)
            {
                ancestor.words.push_back(synsetPtr->words[j]);
            }
            ancestors.push_back(ancestor);
            getAncestors(synsetPtr, ancestors);
            free_syns(freePtr);
            break;
        }
    }
}

vector<NODE> getInfo(char* word, int pos, int type, int whichsense)
{
    vector<NODE> nodes;
    SynsetPtr freePtr = findtheinfo_ds(word, pos, type, whichsense);
    SynsetPtr synsetPtr = freePtr;
    while(synsetPtr)
    {
        NODE node;
        vector<ANCESTOR> ancestors;
        node.hereiam = synsetPtr->hereiam;
        node.defn = string(synsetPtr->defn);
        for (int j = 0; j < synsetPtr->wcount; ++j)
        {
            node.words.push_back(synsetPtr->words[j]);
        }
        getAncestors(synsetPtr, ancestors);
        node.ancestors = ancestors;
        nodes.push_back(node);
        synsetPtr = synsetPtr->nextss;
    }
    free_syns(freePtr);
    return nodes;
}

void print(vector<NODE> &nodes, ofstream &output)
{
    for (int i = 0; i < nodes.size(); ++i)
    {
        output << "        <synset>" << endl;
        output << "            <offset>" << nodes[i].hereiam << "</offset>" << endl;
        output << "            <defination>" << nodes[i].defn << "</defination>" << endl;
        string words = nodes[i].words[0];
        for (int k = 1; k < nodes[i].words.size(); ++k)
        {
            words += ", " + nodes[i].words[k];
        }
        output << "            <words>" << words << "</words>" << endl;
        output << "            <ancestorCount>" << nodes[i].ancestors.size() << "</ancestorCount>" << endl;
        for (int j = 0; j < nodes[i].ancestors.size(); ++j)
        {
            output << "            <ancestor>" << endl;
            output << "                <offset>" << nodes[i].ancestors[j].hereiam << "</offset>" << endl;
            output << "                <defination>" << nodes[i].ancestors[j].defn << "</defination>" << endl;
            words = nodes[i].ancestors[j].words[0];
            for (int k = 1; k < nodes[i].ancestors[j].words.size(); ++k)
            {
                words += ", " + nodes[i].ancestors[j].words[k];
            }
            output << "                <words>" << words << "</words>" << endl;
            output << "            </ancestor>" << endl;
        }
        output << "        </synset>" << endl;
    }
}

long getLeastSubsume(vector<NODE> &nodes)
{
    map<long, int> route;
    for (int i = 0; i < nodes[0].ancestors.size(); ++i)
    {
        route.insert(pair<long, int>(nodes[0].ancestors[i].hereiam, 1));
    }
    for (int i = 1; i < nodes.size(); ++i)
    {
        for (int j = 0; j < nodes[i].ancestors.size(); ++j)
        {
            if (route.find(nodes[i].ancestors[j].hereiam) != route.end())
            {
                route[nodes[i].ancestors[j].hereiam]++;
            }
        }
    }
    for (int i = 0; i < nodes[0].ancestors.size(); ++i)
    {
        if (route[nodes[0].ancestors[i].hereiam] == nodes.size())
        {
            return nodes[0].ancestors[i].hereiam;
        }
    }
    return -1;
}

vector<string> analysisString(string s)
{
    vector<string> ss;
    int start = s.find('"');
    string temp;
    while (start != -1)
    {
        int end = s.find('"', start + 1);
        temp += s.substr(start + 1, end - start - 1) + ". ";
        s = s.substr(0, start) + s.substr(end + 1);
        start = s.find('"');
    }
    ss.push_back(s);
    ss.push_back(temp);
    return ss;
}

string normalize(string s)
{
    string t;
    int start = s.find('_');
    while (start != -1)
    {
        string temp = s.substr(0, start);
        t += temp + " ";
        s = s.substr(start + 1);
        start = s.find('_');
    }
    t += s;
    return t;
}

DOC createDoc(NODE &node, long subsume)
{
    string def;
    string example;
    string words;
    vector<string> temp;
    string tempstr;
    temp = analysisString(node.defn);
    def = temp[0];
    example = temp[1];
    tempstr = normalize(node.words[0]);
    for (int i =  1; i < node.words.size(); ++i)
    {
        tempstr += ", " + normalize(node.words[i]);
    }
    words = tempstr;
    for (int i = 0; i < node.ancestors.size(); ++i)
    {
        if (node.ancestors[i].hereiam == subsume)
        {
            break;
        }
        temp = analysisString(node.ancestors[i].defn);
        def += " " + temp[0];
        example += " " + temp[1];
        tempstr = normalize(node.ancestors[i].words[0]);
        for (int j = 1; j < node.ancestors[i].words.size(); ++j)
        {
            tempstr += ", " + normalize(node.ancestors[i].words[j]);
        }
        words += ", " + tempstr;
    }
    DOC doc;
    doc.def = def;
    doc.example = example;
    doc.words = words;
    return doc;
}

vector<DOC> createDocs(vector<NODE> &nodes)
{
    long subsume = getLeastSubsume(nodes);
    vector<DOC> docs;
    for (int i = 0; i < nodes.size(); ++i)
    {
        DOC doc = createDoc(nodes[i], subsume);
        docs.push_back(doc);
    }
    return docs;
}

int main()
{
    ifstream purposes;
    purposes.open("purpose.suffix.dedup");
    ofstream direct;
    direct.open("direct.xml");
    ofstream docsfile;
    docsfile.open("docs.txt");
    directMultiCount = 0;
    int totalsynset = 0;
    if (wninit()) {		// open database 
	    cerr <<"wn: Fatal error - cannot open WordNet database" << endl;
	    exit (-1);
    }
    string temp;
    int directCount = 0;
    direct << "<xml>" << endl;
    while (getline(purposes, temp))
    {
        char buff[100];
        temp.copy(buff, temp.size());
        buff[temp.size()] = '\0';
        string name(buff);
        direct << "    <item>" << endl;
        direct << "        <name>" << name << "</name>" << endl;
        if (!string(do_is_defined(const_cast<char*>(temp.c_str()))).empty())
        {
            directCount++;
            vector<NODE> nodes = getInfo(const_cast<char*>(temp.c_str()), NOUN, OVERVIEW, ALLSENSES);
            totalsynset += nodes.size();
            if (nodes.size() > 1)
            {
                directMultiCount++;
                vector<DOC> docs = createDocs(nodes);
                docsfile << endl << name << endl;
                for (int i = 0; i < docs.size(); ++i)
                {
                    docsfile << "definition: " <<  docs[i].def << endl;
                    docsfile << "example: " << docs[i].example << endl;
                    docsfile << "words: " << docs[i].words << endl;
                }
            }
            direct << "        <synsetCount>" << nodes.size() << "</synsetCount>" << endl;
            print(nodes, direct);
        }
        else
        {
            direct << "        <synsetCount>0</synsetCount>" << endl;
        }
        direct << "    </item>" << endl;
    }
    direct << "</xml>" << endl;
    docsfile << endl;
    cout << "directMatchNumber = " << directCount << endl;
    cout << "directMatchPolyNumber = " << directMultiCount << endl;
    cout << "averageSynsetNumber = " << totalsynset * 1.0 / directCount << endl;
    purposes.close();
    direct.close();
    docsfile.close();
}
